/**
 * 	Fichier contenant les méthodes de création et traduction de l'arbre.
 *
 *	@file 		arbre.c
 *	@author 	Jacquemin Thibault Marleix Mathieu
 *	@version 	0.5
 *
 */

#ifndef ARBRE
#include "arbre.h"
#endif

/**
 * 	Assemble les arbres passés en paramètres.
 *
 * 	@fn 		void 	assemble(tree parent, tree sons)
 * 	@param[out]	parent	Arbre parent contenant les précédents noeuds.
 *	@param[in]	sons	Abre courant à fusionner avec l'arbre parent.
 */
void assemble(tree parent, tree sons) {
	parent->content.op.nbSons = (parent->content.op.nbSons) + 1;
	parent->content.op.sons = (tree *) realloc(parent->content.op.sons, parent->content.op.nbSons*sizeof(tree));
	if(parent->content.op.sons == NULL){
    	fprintf(stderr,"assemble: allocation of sons return NULL\n");
		exit(EXIT_FAILURE);
	}
	parent->content.op.sons[parent->content.op.nbSons-1] = sons;
}
/**
 *	Crée les noeuds des arbres.
 *
 * 	@fn 		tree 		createNode(int type, char * content, float argc, ...)
 *	@param[in]	type		Type du noeud (voir types définis dans arbre.h)
 *	@param[in]	content		Contenu du noeud de type string ou pourles noeuds opérateurs le type de l'opération.
 *	@param[in]	argc		Contient le nombre d'argument ou pour une feuille de type float, sa valeur.
 *	@return		T			Le noeud crée.
 */
tree createNode(int type, char * content, float argc, ...) {
        tree T = (tree) malloc(sizeof(node));
        if( T == NULL){
        	fprintf(stderr,"createNode: allocation of T return NULL\n");
        	 exit(EXIT_FAILURE);
        }
        T->type = type;

        switch (T->type) {
			case NODEFLOAT:
				if(DEBUG) fprintf(stdout,"creating a node of type %d: NODEFLOAT containing %f\n", T->type, argc);
                T->content.f = argc;
                break;
			case NODEOP:
				if(DEBUG) fprintf(stdout,"creating a node of type %d: NODEOP containing %d with %d arguments\n", T->type,atoi(content) , (int) argc );
                T->content.op.type = atoi(content);
                T->content.op.nbSons = (int) argc;
                T->content.op.sons = (tree *) malloc(T->content.op.nbSons * sizeof(tree));
                if( T->content.op.sons == NULL){
                	fprintf(stderr,"createNode: allocation of T->content.op.sons return NULL\n");
                	exit(EXIT_FAILURE);
                }
                int argi;
                va_list liste;
                va_start(liste, argc);
                for (argi = 0; argi < T->content.op.nbSons; argi++) {
                	T->content.op.sons[argi] = va_arg(liste, tree);
                }
                va_end(liste);
                break;
			case NODEEMPTY:
				if(DEBUG) fprintf(stdout,"creating a node of type %d: ROOT\n", type);
				T->content.op.nbSons = 0;
				T->content.op.sons = NULL;
				break;
			default:
				// Regroupe NODESTRING, NODECOM
				if(DEBUG) fprintf(stdout,"creating a node of type %d containing %s\n", T->type, content);
				T->content.c = content;
				break;
        }
        return T;
}

/**
 * Traduit l'arbre de la structure tree vers le language Poscript.
 *
 * @fn 			void 	interpretNode(tree T,FILE *yyout)
 * @param[in]	T		Arbre à interpréter.
 * @param[in]	yyout	Descripteur de fichier vers le fichier de sortie.
 */
void interpretNode(tree T,FILE *yyout){
	int i;
	switch (T->type) {
	case NODEOP:
		if(DEBUG) fprintf(stdout,"Subtree: type %d of function ",T->type);
		switch (T->content.op.type){
		case OPDRAW:
			if(DEBUG) fprintf(stdout,"draw\n");
			fprintf(yyout,"newpath\n");
			interpretNode(T->content.op.sons[0],yyout);
			interpretNode(T->content.op.sons[1],yyout);
			fprintf(yyout,"moveto\n");
			interpretNode(T->content.op.sons[2],yyout);
			interpretNode(T->content.op.sons[3],yyout);
			fprintf(yyout,"lineto\nstroke\n");
			free(T);
			break;
		case OPASSIGN:
			if(DEBUG) fprintf(stdout,"assign\n");
			fprintf(yyout,"/");
			interpretNode(T->content.op.sons[0],yyout);
			interpretNode(T->content.op.sons[1],yyout);
			fprintf(yyout,"def\n");
			free(T);
			break;
		case OPEQ:
			if(DEBUG) fprintf(stdout,"equal\n");
			interpretNode(T->content.op.sons[0],yyout);
			interpretNode(T->content.op.sons[1],yyout);
			fprintf(yyout,"eq ");
			free(T);
			break;
		case OPNEQ:
			if(DEBUG) fprintf(stdout,"not equal\n");
			interpretNode(T->content.op.sons[0],yyout);
			interpretNode(T->content.op.sons[1],yyout);
			fprintf(yyout,"ne ");
			free(T);
			break;
		case OPLEQ:
			if(DEBUG) fprintf(stdout,"less or equal\n");
			interpretNode(T->content.op.sons[0],yyout);
			interpretNode(T->content.op.sons[1],yyout);
			fprintf(yyout,"le ");
			break;
		case OPGEQ:
			if(DEBUG) fprintf(stdout,"great or equal\n");
			interpretNode(T->content.op.sons[0],yyout);
			interpretNode(T->content.op.sons[1],yyout);
			fprintf(yyout,"ge ");
			free(T);
			break;
		case OPGT:
			if(DEBUG) fprintf(stdout,"great\n");
			interpretNode(T->content.op.sons[0],yyout);
			interpretNode(T->content.op.sons[1],yyout);
			fprintf(yyout,"gt ");
			free(T);
			break;
		case OPLT:
			if(DEBUG) fprintf(stdout,"less\n");
			interpretNode(T->content.op.sons[0],yyout);
			interpretNode(T->content.op.sons[1],yyout);
			fprintf(yyout,"lt ");
			free(T);
			break;
		case OPMINUS:
			if(DEBUG) fprintf(stdout,"minus\n");
			interpretNode(T->content.op.sons[0],yyout);
			interpretNode(T->content.op.sons[1],yyout);
			fprintf(yyout,"sub ");
			free(T);
			break;
		case OPPLUS:
			if(DEBUG) fprintf(stdout,"plus\n");
			interpretNode(T->content.op.sons[0],yyout);
			interpretNode(T->content.op.sons[1],yyout);
			fprintf(yyout,"add ");
			free(T);
			break;
		case OPMUL:
			if(DEBUG) fprintf(stdout,"mul\n");
			interpretNode(T->content.op.sons[0],yyout);
			interpretNode(T->content.op.sons[1],yyout);
			fprintf(yyout,"mul ");
			free(T);
			break;
		case OPDIV:
			if(DEBUG) fprintf(stdout,"div\n");
			interpretNode(T->content.op.sons[0],yyout);
			interpretNode(T->content.op.sons[1],yyout);
			fprintf(yyout,"div ");
			free(T);
			break;
		case OPLOOP:
			if(DEBUG) fprintf(stdout,"for\n");
			interpretNode(T->content.op.sons[0],yyout);
			fprintf(yyout,"{\n");
			interpretNode(T->content.op.sons[1],yyout);
			fprintf(yyout,"{\n");
			interpretNode(T->content.op.sons[3],yyout);
			fprintf(yyout,"}\n{exit} ifelse\n");
			interpretNode(T->content.op.sons[2],yyout);
			fprintf(yyout,"} loop\n");
			free(T);
			break;
		case OPIF:
			if(DEBUG) fprintf(stdout,"if\n");
			interpretNode(T->content.op.sons[0],yyout);
			fprintf(yyout,"{\n");
			interpretNode(T->content.op.sons[1],yyout);
			fprintf(yyout,"} if\n");
			free(T);
			break;
		case OPELSE:
			if(DEBUG) fprintf(stdout,"else\n");
			interpretNode(T->content.op.sons[0],yyout);
			fprintf(yyout,"{\n");
			interpretNode(T->content.op.sons[1],yyout);
			fprintf(yyout,"}\n{\n");
			interpretNode(T->content.op.sons[2],yyout);
			fprintf(yyout,"} ifelse\n");
			free(T);
			break;
		case OPDEF:
			if(DEBUG) fprintf(stdout,"definition of function\n");
			fprintf(yyout,"/");
			interpretNode(T->content.op.sons[0],yyout);
			fprintf(yyout,"{\n %d dict begin \n", T->content.op.sons[1]->content.op.nbSons);
			for (i = T->content.op.sons[1]->content.op.nbSons-1; i >= 0  ; i--) {
				fprintf(yyout,"/");
				interpretNode(T->content.op.sons[1]->content.op.sons[i],yyout);
				fprintf(yyout, "exch def\n");
			}
			fprintf(yyout,"gsave\n");
			interpretNode(T->content.op.sons[2],yyout);
			fprintf(yyout,"grestore\n end\n} def\n");
			free(T);
			break;
		case OPFUNC:
			if(DEBUG) fprintf(stdout,"call of function\n");
			for (i = 0; i < T->content.op.sons[1]->content.op.nbSons ; i++) {
				interpretNode(T->content.op.sons[1]->content.op.sons[i],yyout);
			}
			interpretNode(T->content.op.sons[0],yyout);
			fprintf(yyout,"\n");
			free(T);
			break;
		default:
			fprintf(stderr,"InterpretNode: case default error\n");
			free(T);
			break;
		}
		break;
	case NODEFLOAT:
		if(DEBUG) fprintf(stdout,"Leave with float\n");
		fprintf(yyout,"%f ",T->content.f);
		free(T);
		break;
	case NODESTRING:
		if(DEBUG) fprintf(stdout,"Leave with string\n");
		fprintf(yyout,"%s ",T->content.c);
		free(T);
		break;
	case NODEEMPTY:
		if(DEBUG) fprintf(stdout,"Root with %d\n",T->content.op.nbSons);
		for (i = 0; i <T->content.op.nbSons; i++) {
			interpretNode(T->content.op.sons[i],yyout);
		}
		free(T);
		break;
	case NODECOM:
		if(DEBUG) fprintf(stdout,"leave with commentary\n");
		fprintf(yyout,"%%%s\n",T->content.c);
		free(T);
		break;
	default:
		fprintf(stderr,"InterpretNode: case default error\n");
		free(T);
		break;
	}
}
